/* Copyright (c) 2025 Beijing Semidrive Technology Corporation
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/********************************************************************************************************
 * \file     Mcal_Delay.c                                                                               *
 * \brief    AUTOSAR 4.3.1 MCAL McalLib Driver                                                          *
 *                                                                                                      *
 * <table>                                                                                              *
 * <tr><th>Date           <th>Version                                                                   *
 * <tr><td>2025/01/06     <td>1.0.0                                                                     *
 * </table>                                                                                             *
 *******************************************************************************************************/
#ifdef __cplusplus
extern "C" {
#endif
/********************************************************************************************************
 *                                      Include header files                                            *
 *******************************************************************************************************/
#include "Mcal_Delay.h"
#include "Mcal_Fault.h"
/********************************************************************************************************
 *                                 Private Macro definition                                             *
 *******************************************************************************************************/
#define PMU_EN_CYCLE  0x80000000U
/********************************************************************************************************
 *                                  Private Function Declarations                                       *
 *******************************************************************************************************/
extern void write_pmcr(uint32 x);
extern void write_pmuserenr(uint32 x);
extern void write_pmcntenset(uint32 x);
extern uint32 read_pmcnten(void);
/********************************************************************************************************
 *                                  Global Function Declarations                                        *
 *******************************************************************************************************/
#define MCALLIB_START_SEC_CODE
#include "McalLib_MemMap.h"
/** *****************************************************************************************************
 * \brief This function is to enable PMU counter.
 *        This function can only called after Mcu_InitClock();
 *
 * \verbatim
 * Syntax             : void Mcal_PmuCounterInit
 *                      (
 *                          void
 *                      )
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : None.
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : None
 *
 * Description        : This function is to enable PMU counter.
 * \endverbatim
 * Traceability       : None
 *******************************************************************************************************/
void Mcal_PmuCounterInit(void)
{
    /* 10 Turn on cycle counter*/
    write_pmcntenset(PMU_EN_CYCLE);
    /* 20 Turn on Performance Monitor, set cycle counter to 'increase 1 tick every 64 cpu cycle' */
    write_pmcr(0x9);
    /* 30 pmu register can be accessed at user mode when the pmuserenr.en enabled */
    write_pmuserenr(0x1);
}
/** *****************************************************************************************************
 * \brief This function is to check whether the counter is enabled.
 *
 * \verbatim
 * Syntax             : static boolean Mcal_Cnt_IsEnabled
 *                      (
 *                          void
 *                      )
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : None.
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : TRUE: PMU counter is enable.
 *                      FALSE: PMU counter is disable
 *
 * Description        : This function is to check whether the counter is enabled.
 * \endverbatim
 * Traceability       : None
 *******************************************************************************************************/
static boolean Mcal_Cnt_IsEnabled(void)
{
    boolean ret;

    uint32 pmcn = read_pmcnten();

    MCALLIBHook_Mcal_Cnt_IsEnabled();

    if (PMU_EN_CYCLE == (pmcn & PMU_EN_CYCLE))
    {
        ret = TRUE;
    }
    else
    {
        ret = FALSE;
    }

    return ret;
}
/** *****************************************************************************************************
 * \brief This function is convert us to ticks.
 *
 * \verbatim
 * Syntax             : uint32 Mcal_UsToTicks
 *                      (
 *                          uint32 us
 *                      )
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : us - How many us will be convert to ticks.
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : The ticks is converted out.
 *
 * Description        : This function is convert us to ticks.
 * \endverbatim
 * Traceability       : None
 *******************************************************************************************************/
uint32 Mcal_UsToTicks(uint32 us)
{
    return PMU_US_TO_TICK(us);
}
/** *****************************************************************************************************
 * \brief This function is convert ticks to us.
 *
 * \verbatim
 * Syntax             : uint32 Mcal_TicksToUs
 *                      (
 *                          uint32 ticks
 *                      )
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : ticks - How many ticks will be convert to us.
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : The time is converted out.
 *
 * Description        : This function is convert ticks to us.
 * \endverbatim
 * Traceability       : None
 *******************************************************************************************************/
uint32 Mcal_TicksToUs(uint32 ticks)
{
    return (uint32)(((ticks) * 16U) / (MCAL_CPU_FREQUENCY_MHZ / 4000000U));
}
/** *****************************************************************************************************
 * \brief This udelay function based on performance monitor unit.
 *
 * \verbatim
 * Syntax             : Std_ReturnType Mcal_udelay
 *                      (
 *                          uint32 us
 *                      )
 *
 * Service ID[hex]    : None
 *
 * Sync/Async         : Synchronous
 *
 * Reentrancy         : Reentrant
 *
 * Parameters (in)    : us - How many us will be delayed.
 *
 * Parameters (inout) : None
 *
 * Parameters (out)   : None
 *
 * Return value       : E_OK: Delay is OK.
 *                      MCALLIB_E_PMU_CNT: PMU counter is disable.
 *                      MCALLIB_E_PARAMS: Input parameter is error.
 *                      MCALLIB_E_TIMEOUT: Read counter timeout.
 *
 * Description        : Delay time in us. max time is 21 s.
 * \endverbatim
 * Traceability       : None
 *******************************************************************************************************/
Std_ReturnType Mcal_udelay(uint32 us)
{
    uint32 tickTo, startTick, currentTick;
    uint32 tmt = 0U;
    Std_ReturnType errStatus = E_OK;

    if (0U == Mcal_Cnt_IsEnabled())
    {
        errStatus = MCALLIB_E_PMU_CNT;
    }
    else if ((0U == us) || (us > 21000000UL))
    {
        errStatus = MCALLIB_E_PARAMS;
    }
    else
    {
        startTick = read_pmccntr();

        /* 10 make sure cycled information: 100 loops takes ~5us */
        do
        {
            tmt++;
            currentTick = read_pmccntr();
            MCALLIBHook_Mcal_udelay();
        }
        while ((startTick == currentTick) && (tmt < 100U));

        if (tmt >= 100U)
        {
            errStatus = MCALLIB_E_TIMEOUT;
        }
        else
        {
            /* 20 timeout check is not a must here, just in case the pmccntr abnormal */
            tickTo = startTick + PMU_US_TO_TICK(us);

            if (tickTo > startTick)
            {
                /* 30 tickTo not overflowed */
                do
                {
                    currentTick = read_pmccntr();
                }
                while ((currentTick < tickTo)
                        && (currentTick > startTick));
            }
            else
            {
                /* 40 currentTick overflowed */
                do
                {
                    currentTick = read_pmccntr();
                    MCALLIBHook_Mcal_udelayOverFlow();
                }
                while ((currentTick > startTick) || (currentTick < tickTo));
            }
        }
    }

    return errStatus;
}

#define MCALLIB_STOP_SEC_CODE
#include "McalLib_MemMap.h"
#ifdef __cplusplus
}
#endif
/* End of file */
